#include "TextureManager.hpp"

namespace zen {

	namespace video {

		PTexture TextureManager::loadPixmap(const std::string &id, const Pixmap &pixmap, TextureWrap wrap, TextureMinFilter minFilter, TextureMagFilter magFilter, GLenum format) {
			zen_call("Texture::loadPixmap");
			TexturesConstIterator iter = textures.find(id);
			if (iter != textures.end())
				return iter->second;
			PTexture tmp = createTexture(pixmap, wrap, minFilter, magFilter, format);
			tmp->setFileName(id);
			textures.insert(TexturesEntry(id, tmp));
			return tmp;
		}

		PTexture TextureManager::loadFile(const std::string &id, const std::string &fileName, TextureWrap wrap, TextureMinFilter minFilter, TextureMagFilter magFilter, GLenum format) {
			zen_call("TextureManager::loadFile");
			TexturesConstIterator iter = textures.find(id);
			if (iter != textures.end())
				return iter->second;
			Pixmap pix;
			if (!pix.load(fileName)) {
				zen_log("Error: Could not load texture file \"" + fileName + "\".");
				return PTexture();
			}
			PTexture tmp = createTexture(pix, wrap, minFilter, magFilter, format);
			tmp->setFileName(fileName);
			textures.insert(TexturesEntry(id, tmp));
			return tmp;
		}

		PTexture TextureManager::createTexture(const Pixmap &pix, TextureWrap wrap, TextureMinFilter minFilter, TextureMagFilter magFilter, GLenum format) {
			zen_call("Texture::createTexture");
			if (pix.getData() == nullptr)
				return PTexture();
			if (format == 0)
				format = pix.getFormat();
			if (format == GL_BGR)
				format = GL_RGB;
			else if (format == GL_BGRA)
				format = GL_RGBA;
			GLenum type = pix.getDepth() > 1 ? GL_TEXTURE_3D : pix.getHeight() > 1 ? GL_TEXTURE_2D : GL_TEXTURE_1D;
			bool texEnabled = glIsEnabled(type) == GL_TRUE;
			if (!texEnabled)
				glEnable(type); // Texturing must be enabled before texture upload.
			GLuint textureId;
			glGenTextures(1, &textureId);
			if (textureId == 0)
				return PTexture();
			glBindTexture(type, textureId);
			glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
			//Set texture parameters
			glTexParameteri(type, GL_TEXTURE_WRAP_S, wrap);
			glTexParameteri(type, GL_TEXTURE_WRAP_T, wrap);
			glTexParameteri(type, GL_TEXTURE_MAG_FILTER, (GLint)magFilter);
			glTexParameteri(type, GL_TEXTURE_MIN_FILTER, (GLint)minFilter);
			glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
			//Upload the texture
			switch (type) {
				case GL_TEXTURE_1D:
					glTexImage1D(type,
						0,                  //level of detail
						format,             //internal format
						pix.getWidth(),     //image width
						0,                  //use borders?
						pix.getFormat(),    //texture format
						GL_UNSIGNED_BYTE,   //data type
						pix.getData()
					);
					break;
				case GL_TEXTURE_2D:
					glTexImage2D(type,
						0,                  //level of detail
						format,             //internal format
						pix.getWidth(),     //image width
						pix.getHeight(),    //image height
						0,                  //use borders?
						pix.getFormat(),    //texture format
						GL_UNSIGNED_BYTE,   //data type
						pix.getData()
					);
					break;
				case GL_TEXTURE_3D:
					glTexImage3D(type,
						0,                  //level of detail
						format,             //internal format
						pix.getWidth(),     //image width
						pix.getHeight(),    //image height
						pix.getDepth(),		//image depth
						0,                  //use borders?
						pix.getFormat(),    //texture format
						GL_UNSIGNED_BYTE,   //data type
						pix.getData()
					);
					break;
			}
			if (!texEnabled)
				glDisable(type); // Restore the texturing state
			PTexture texture(new Texture(type, textureId));
			return texture;
		}

	} // namespace video

} // namespace zen